﻿/*
 * © 2010 The Android Open Source Project
 *
 * Лицензировано по лицензии Apache License, версия 2.0 ( "Лицензия");
 *этот файл можно использовать только в соответствии с лицензией.
 *Копию лицензии можно получить на веб-сайте
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *Если только не требуется в соответствии с применимым законодательством или согласовано в письменном виде, программное обеспечение
 * распространяется в рамках лицензии на УСЛОВИЯХ "КАК ЕСТЬ",
 * БЕЗ ГАРАНТИЙ И УСЛОВИЙ ЛЮБОГО РОДА, явно выраженных и подразумеваемых.
 * См. лицензию для получения информации об определенных разрешениях по использованию языка и
 * ограничениях в рамках лицензии.
 *
 */

#ifndef _ANDROID_NATIVE_APP_GLUE_H
#define _ANDROID_NATIVE_APP_GLUE_H

#include <poll.h>
#include <pthread.h>
#include <sched.h>

#include <android/configuration.h>
#include <android/looper.h>
#include <android/native_activity.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * Интерфейс NativeActivity, предоставленный <android/native_activity.h>,
 * основан на наборе предоставленных приложением обратных вызовов, которые вызываются
 *основным потоком действия при возникновении определенных событий.
 *
 * Это означает, что ни один из данных обратных вызовов _не_ _должен_ блокироваться, иначе
 * существует риск принудительного закрытия приложения системой. Эта модель программирования
 * прямая, простая, но имеет ограничения.
 *
 * Статическая библиотека threaded_native_app используется для обеспечения другой
 * модели выполнения, в которой приложение может реализовать свой собственный цикл главного события
 * в другом потоке вместо этого. Это работает так:
 *
 * 1/ Приложение должно предоставить функцию с именем android_main(), которая
 *    будет вызываться при создании действия в новом потоке,
 *    отличающемся от основного потока действия.
 *
 * 2/ android_main() получает указатель на допустимую структуру android_app,
 *   которая содержит ссылки на другие важные объекты, например экземпляр объекта
 *    ANativeActivity, где выполняется приложение.
 *
 * 3/ Объект android_app содержит экземпляр ALooper, который уже
 *    ожидает две важных вещи:
 *
 *      - событий жизненного цикла действия (например, "pause", "resume"). См. объявления APP_CMD_XXX
 * ниже.
 *
 *      - входных событий, поступающих из очереди AInputQueue, присоединенной к действию.
 *
 *    Каждое из этих событий соответствует идентификатору ALooper, возвращенному 
 *    ALooper_pollOnce со значениями LOOPER_ID_MAIN и LOOPER_ID_INPUT,
 *, соответственно.
 *
 *    Ваше приложение может использовать тот же ALooper для прослушивания дополнительных
 *    дескрипторов файла. Они могут быть основаны либо на обратных вызовах, либо поступают с идентификаторами возврата,
 * начинающимися с  LOOPER_ID_USER.
 *
 * 4/ При получении события LOOPER_ID_MAIN или LOOPER_ID_INPUT 
 *   возвращенные данные будут указывать на структуру android_poll_source. Для нее
 *    можно вызвать функцию process() и заполнить android_app->onAppCmd
 *    и android_app->onInputEvent, для того чтобы они вызывались для вашей собственной обработки
 *    события.
 *
 *    Вместо этого можно вызвать функции нижнего уровня для чтения и обработки
 *    данных непосредственно...  посмотрите на реализации process_cmd() и process_input()
 *    в приклеивании, чтобы выяснить, как это делается.
 *
 * См. пример "native-activity" в NDK с
 * полной демонстрацией использования. Также посмотрите JavaDoc в NativeActivity.
 */

struct android_app;

/**
 * Данные, связанные с ALooper fd, которые будут возвращаться как outData
 * при готовности данных в этом источнике.
 */
struct android_poll_source {
    // Идентификатор данного источника. Может быть LOOPER_ID_MAIN или
    // LOOPER_ID_INPUT.
    int32_t id;

    // android_app, с которым связан данный идентификатор.
    struct android_app* app;

    // Функция, вызываемая для стандартной обработки данных из
    // этого источника.
    void (*process)(struct android_app* app, struct android_poll_source* source);
};

/**
 * Это интерфейс стандартного кода приклеивания поточного
 * приложения. В этой модели код приложения выполняется
 * в своем собственном потоке, отдельном от основного потока процесса.
 * Не требуется связь данного потока с ВМ Java
 *, хотя это необходимо для выполнения вызовов JNI любых
 * объектов Java.
 */
struct android_app {
    // Приложение может поместить указатель на свой собственный объект состояния
    // здесь, если нужно.
    void* userData;

    // Введите здесь код функции для обработки основных команд приложения (APP_CMD_*)
    void (*onAppCmd)(struct android_app* app, int32_t cmd);

    // Введите здесь код функции для обработки входных событий. Сейчас
    // событие уже было предварительно отправлено и будет завершено при
    // возврате. Верните 1, если событие обработано, 0 — для любой диспетчеризации
    // по умолчанию.
    int32_t (*onInputEvent)(struct android_app* app, AInputEvent* event);

    // Экземпляр объекта ANativeActivity, в котором выполняется это приложение.
    ANativeActivity* activity;

    // Текущая конфигурация, в которой выполняется это приложение.
    AConfiguration* config;

    // Это последнее сохраненное состояние экземпляра, предоставленное во время создания.
    // Значение равно NULL, если состояния не было. Можно использовать это по мере необходимости;
    // память останется доступной до вызова android_app_exec_cmd() для
    // APP_CMD_RESUME, после чего она будет освобождена, а savedState получит значение NULL.
    // Эти переменные необходимо изменять только при обработке APP_CMD_SAVE_STATE,
    // когда их значения будут инициализироваться в NULL и можно будет выполнить malloc для
    // состояния и поместить здесь информацию. В этом случае память будет
    // освобождена позднее.
    void* savedState;
    size_t savedStateSize;

    // ALooper, связанный с потоком приложения.
    ALooper* looper;

    // Если значение не равно NULL, то это входная очередь, из которой приложение будет
    // получать входные события пользователя.
    AInputQueue* inputQueue;

    // Если значение не равно NULL, то это поверхность окна, в котором приложение может рисовать.
    ANativeWindow* window;

    // Текущий прямоугольник содержимого окна. Это область, в которой
    // должно помещаться содержимое окна, чтобы его видел пользователь.
    ARect contentRect;

    // Текущее состояние действия приложения. Может быть APP_CMD_START,
    // APP_CMD_RESUME, APP_CMD_PAUSE или APP_CMD_STOP; см. ниже.
    int activityState;

    // Значение не равно нулю, когда NativeActivity приложения
    // разрушается и ожидает завершения потока приложения.
    int destroyRequested;

    // -------------------------------------------------
    // Ниже показан "частная" реализация кода прилипания.

    pthread_mutex_t mutex;
    pthread_cond_t cond;

    int msgread;
    int msgwrite;

    pthread_t thread;

    struct android_poll_source cmdPollSource;
    struct android_poll_source inputPollSource;

    int running;
    int stateSaved;
    int destroyed;
    int redrawNeeded;
    AInputQueue* pendingInputQueue;
    ANativeWindow* pendingWindow;
    ARect pendingContentRect;
};

enum {
    /**
     * Идентификатор данных Looper команд, поступающих из основного потока приложения, который
     * возвращается как идентификатор от ALooper_pollOnce().  Данные для этого идентификатора
     * являются указателем на структуру android_poll_source.
     * Их можно извлечь и обработать с помощью android_app_read_cmd()
     * и android_app_exec_cmd().
     */
    LOOPER_ID_MAIN = 1,

    /**
     * Идентификатор данных Looper событий, поступающий из AInputQueue окна
     * приложения, который возвращается как идентификатор из
     * ALooper_pollOnce(). Данные этого идентификатора являются указателем на структуру
     * android_poll_source. Их можно прочитать через объект inputQueue
     * приложения android_app.
     */
    LOOPER_ID_INPUT = 2,

    /**
     * Запуск определяемых пользователем идентификаторов ALooper.
     */
    LOOPER_ID_USER = 3,
};

enum {
    /**
     * Команда из основного потока: AInputQueue изменена.  После обработки
     * этой команды android_app->inputQueue будет обновлена в новую очередь
     * (или NULL).
     */
    APP_CMD_INPUT_CHANGED,

    /**
     * Команда из основного потока: новое окно ANativeWindow готово к использованию. После
     * получения этой команды окно android_app-> будет содержать новую поверхность
     *окна.
     */
    APP_CMD_INIT_WINDOW,

    /**
     * Команда из основного потока: существующее окно ANativeWindow необходимо
     * прекратить. После получения этой команды окно android_app->по-прежнему
     * содержит существующее окно; после вызова android_app_exec_cmd
     * оно получит значение NULL.
     */
    APP_CMD_TERM_WINDOW,

    /**
     * Команда из основного потока: текущее окно ANativeWindow изменило размер.
     * Перерисуйте согласно новом размеру.
     */
    APP_CMD_WINDOW_RESIZED,

    /**
     * Команда из основного потока: системе необходимо, чтобы текущее окно ANativeWindow
     * было перерисовано. Необходимо перерисовать окно перед ее передачей в
     * android_app_exec_cmd(), чтобы избежать переходных сбоев рисования.
     */
    APP_CMD_WINDOW_REDRAW_NEEDED,

    /**
     * Команда из основного потока: область содержимого окна изменена
     * таким образом, что из функционального ввода окно показывается или скрывается. Можно
     * найти новый прямоугольник содержимого в android_app::contentRect.
     */
    APP_CMD_CONTENT_RECT_CHANGED,

    /**
     * Команда из основного потока: окно действия приложения получило
     * фокус ввода.
     */
    APP_CMD_GAINED_FOCUS,

    /**
     * Команда из основного потока: окно действия приложения потеряло
     * фокус ввода.
     */
    APP_CMD_LOST_FOCUS,

    /**
     * Команда из основного потока: изменена текущая  конфигурация устройства.
     */
    APP_CMD_CONFIG_CHANGED,

    /**
     * Команда из основного потока: системе не хватает памяти.
     * Попробуйте уменьшить использование памяти.
     */
    APP_CMD_LOW_MEMORY,

    /**
     * Команда из основного потока: действие приложения было запущено.
     */
    APP_CMD_START,

    /**
     * Команда из основного потока: действие приложения было возобновлено.
     */
    APP_CMD_RESUME,

    /**
     * Команда из основного потока: приложение должно создать новое сохраненное состояние
     * для себя, чтобы восстанавливаться из него позднее в случае необходимости. Если вы сохранили состояние,
     * выделите его с использованием malloc и поместите в android_app.savedState с
     * размером android_app.savedStateSize.  Память будет освобождена
     * позднее.
     */
    APP_CMD_SAVE_STATE,

    /**
     * Команда из основного потока: пауза в действии приложения.
     */
    APP_CMD_PAUSE,

    /**
     * Команда из основного потока: действие приложения было остановлено.
     */
    APP_CMD_STOP,

    /**
     * Команда из основного потока: действие приложения уничтожается,
     * и ожидает очистки потока приложения и выхода перед обработкой.
     */
    APP_CMD_DESTROY,
};

/**
 * Вызовите, когда ALooper_pollAll() возвращает LOOPER_ID_MAIN, при чтении следующего сообщения команды
 *приложения.
 */
int8_t android_app_read_cmd(struct android_app* android_app);

/**
 * Вызовите с помощью команды, возвращенной android_app_read_cmd() для выполнения
 * начальной предварительной обработки данной команды. Можно выполнить собственные
 * действия для команды после вызова этой функции.
 */
void android_app_pre_exec_cmd(struct android_app* android_app, int8_t cmd);

/**
 * Вызовите с помощью команды, возвращенной android_app_read_cmd(), для
 * окончательной предварительной обработки данной команды. Необходимо завершить собственные
 * действия с командой до вызова этой функции.
 */
void android_app_post_exec_cmd(struct android_app* android_app, int8_t cmd);

/**
 * Это функция, которую должен реализовать код приложения, представляет собой
 * главный вход в приложение.
 */
extern void android_main(struct android_app* app);

#ifdef __cplusplus
}
#endif

#endif /* _ANDROID_NATIVE_APP_GLUE_H */
